/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef _CARINA_ATTRIBUTE_HH_
#define _CARINA_ATTRIBUTE_HH_

#include "carina/common/global.hh"
#include "carina/common/containers.hh"
#include "carina/common/exception.hh"
#include "carina/common/memory.hh"

// TODO: split
#include "carina/renderer-base.hh"

namespace Carina
{
/*! \brief Specifies what type of value is this, so that the correct widget is chosen for GUI applications. */
enum VarRange
{
    CE_RANGE_NONE,				/*!< Unlimited value. Used for most of the complex types or scalar values that could
								 *   take any value.
								 */
    CE_RANGE_CLAMPED,			//!< Limited in the range of [-1 ... 1].
    CE_RANGE_CLAMPED_POSITIVE,	//!< Limited in the range of [0 ... 1].
    CE_RANGE_NORMALIZED			//!< Used for vector values that must be unit size.
};

/*! \brief Just a placeholder type used for specifying that something is unused */
struct NullType {};

/*! \brief Placeholder type for container types */
template<class U, class A> class NullContTemplate;
typedef NullContTemplate<NullType, NullType> NullContainer;

/*! \brief The basic type which describes information about an attribute.
 *
 *  There are more specialized types for most cases that you should use
 *  instead. The whole system is built so that you don't need to inherit
 *  from any type for the most trivial cases.
 */
class AttributeBase
{
public:
	//! Constructor.
    AttributeBase() {}

	//! Destructor.
    virtual ~AttributeBase() {}

	//! Returns whether the attribute is described by a container type, e.g. list or array.
    virtual bool isContainer() const=0;

	//! Returns what kind of variable type represents a single element of this attribute.
    virtual UniformVarType getType() const=0;

	//! Returns the range of each element of this attribute.
    virtual VarRange getRange() const=0;

	//! Returns the name of this attribute for searching purposes or GUI visualization.
    virtual string getName() const=0;

	//! Returns how many elements this attribute contains.
	virtual size_t size() const=0;
};

typedef shared_ptr<AttributeBase> AttributePtr;
typedef shared_ptr<const AttributeBase> AttributeConstPtr;

template<class T>
class TypedAttribute: public AttributeBase
{
public:
	typedef T attribute_type;

    virtual bool isContainer() const { return false; }
	virtual size_t size() const { return 1; }

	virtual void setValue(const attribute_type* data, size_t N=1)=0;
    virtual void getValue(attribute_type* data)=0;
};

template<class T> 
struct IsAttributeContainer
{
	static const bool value = false;
};

template<class T, class TAlloc>
struct IsAttributeContainer<DataVector<T, TAlloc>>
{
	static const bool value = true;
};

template<class T, class TAlloc>
struct IsAttributeContainer<DynamicArray<T, TAlloc>>
{
	static const bool value = true;
};

template<class T, class TAttrName, VarRange var_range, bool TIsContainer = IsAttributeContainer<T>::value>
class Attribute: public TypedAttribute<T>
{
public:
	typedef T attribute_type;
protected:
    attribute_type&   m_Value;
public:
    Attribute(attribute_type& var)
		:	m_Value(var) {}

    attribute_type* operator->() { return &m_Value; }
    Attribute& operator=(const attribute_type& t) { m_Value = t; return *this; }
    operator attribute_type&() { return m_Value; }
    operator const attribute_type&() const { return m_Value; }

    virtual void setValue(const attribute_type* data, size_t) { m_Value = *data; }
    virtual void getValue(attribute_type* data) { *data = m_Value; }

    virtual UniformVarType getType() const { return UVTInfo<T>::value(); }
    virtual VarRange getRange() const { return var_range; }
    virtual string getName() const { return TAttrName::name(); }
};

template<template <class U, class A> class TCont, class T, class TAlloc, class TAttrName, VarRange var_range>
class Attribute<TCont<T, TAlloc>, TAttrName, var_range, true>: public TypedAttribute<T>
{
public:
    typedef TCont<T, TAlloc> attribute_type;
private:
    attribute_type&    m_Value;
public:
    Attribute(attribute_type& arr)
		:	m_Value(arr) {}

    attribute_type* operator->() { return &m_Value; }
    Attribute& operator=(const attribute_type& t) { m_Value = t; return *this; }
    operator attribute_type&() { return m_Value; }
    operator attribute_type() const { return m_Value; }

    virtual size_t size() const { return m_Value.size(); }
    virtual void setValue(const T* data, size_t N) { m_Value.assign(data, N); }
    virtual void getValue(T* data) { std::copy(m_Value.begin(), m_Value.end(), data); }

    virtual UniformVarType getType() const { return UVTInfo<T>::value(); }
    virtual VarRange getRange() const { return var_range; }
    virtual string getName() const { return TAttrName::name(); }
};

class AttributeList
{
    DataVector<AttributePtr> m_Attributes;
public:
    AttributeList();
     ~AttributeList();
	 
	template<class T, class TAlloc>
    void getAttributeValue(size_t idx, DynamicArray<T, TAlloc>& t)
    {
		TypedAttribute<T>& attr = static_cast<TypedAttribute<T>&>(*m_Attributes[idx]);
        if(UVTInfo<T>::value() != attr.getType() || !attr.isContainer())
            THROW_EXCEPTION("Invalid type requested for attribute: " + attr.getName());
        t.resize(attr.size());
        attr.getValue(t.get());
    }

	template<class T>
    void getAttributeValue(size_t idx, T& t)
    {
        TypedAttribute<T>& attr = static_cast<TypedAttribute<T>&>(*m_Attributes[idx]);
        if(UVTInfo<T>::value() != attr.getType() || attr.isContainer())
            THROW_EXCEPTION("Invalid type requested for attribute: " + attr.getName());
        attr.getValue(&t);
    }

	template<class T>
    void setAttributeValue(const string& name, const T& t)
    {
        for(size_t i = 0; i < m_Attributes.size(); ++i)
            if(m_Attributes[i]->getName() == name)
            {
                setAttributeValue(i, t);
                return;
            }
        THROW_EXCEPTION("Unknown attribute: " + name);
    }

	template<class T, class TAlloc>
    void setAttributeValue(size_t idx, const DynamicArray<T, TAlloc>& t)
    {
        setArrayAttributeValue(idx, t.get(), t.size());
    }

    template<class T>
    void setArrayAttributeValue(size_t idx, const T* t, size_t N)
    {
        TypedAttribute<T>& attr = static_cast<TypedAttribute<T>&>(*m_Attributes[idx]);
        if(UVTInfo<T>::value() != attr.getType() || !attr.isContainer())
            THROW_EXCEPTION("Invalid type requested for attribute: " + attr.getName());
        attr.setValue(t, N);
    }

	template<class T>
    void setAttributeValue(size_t idx, const T& t)
    {
        TypedAttribute<T>& attr = static_cast<TypedAttribute<T>&>(*m_Attributes[idx]);
        if(UVTInfo<T>::value() != attr.getType() || attr.isContainer())
            THROW_EXCEPTION("Invalid type requested for attribute: " + attr.getName());
        attr.setValue(&t);
    }

    void addAttribute(const AttributePtr& attr);
    
    inline AttributePtr getAttribute(size_t idx) { return m_Attributes[idx]; }
    inline AttributeConstPtr getAttribute(size_t idx) const { return m_Attributes[idx]; }
    
    size_t getAttributeCount() const;
};

typedef shared_ptr<AttributeList> AttributeListPtr;
typedef shared_ptr<const AttributeList> AttributeListConstPtr;

/*! \brief The base type used only for the overly generic polymorphic attributes API.
 *
 *  It brings a virtual destructor to your class. Which means that it is not suitable for
 *  temporary data. However, the whole system is not meant to be used in this case.
 */
class Configurable
{
public:
	//! Default constructor.
	Configurable();

	//! Destructor.
	virtual ~Configurable();

	/*! \brief Appends the list of attributes to specified list.
	 *
	 *  It is expected that the list is created outside by using GetAttributeList. Then
	 *  every function in the configurable chain appends all of its attributes.
	 */
	virtual void getAttributeList(const AttributeListPtr& attr_list) = 0;
};

typedef shared_ptr<Configurable> ConfigurablePtr;
typedef shared_ptr<const Configurable> ConfigurableConstPtr;

//! This type is used for terminating an attribute chain. In other words it stops the appending of attributes.
struct NullBaseClass
{
	inline static void getAttributeList(const AttributeListPtr&) {}
};

//! That's the preferred method of taking the attribute list of a the specified object.
inline AttributeListPtr GetAttributeList(Configurable& conf)
{
	AttributeListPtr attr_list = make_aligned_shared<AttributeList>(); 
	conf.getAttributeList(attr_list);
	return attr_list;
}

/*! \brief Extracts an automatically generated attribute. Don't use it for your own creations.
 *  \param attr_var_name	the name of the attribute that must be "queried".
 */
#define CE_GET_ATTRIBUTE(attr_var_name) __m_##attr_var_name

/*! \brief Shorthand macro for creating a type which carries a specified name as a string.
 *  \param attr_var_name	the name of the type that is going to carry the string.
 *	\param attr_name		the string that is going to be associated to the type.
 */
#define CE_DECLARE_NAME_TYPE(attr_var_name, attr_name) struct attr_var_name { inline static string name() { return attr_name; } }

/*! \brief Defines attribute that does not have a setter.
 *  \warning That does not mean that it could not be initialized through the attributes API.
 *	\param attr_type		the basic type of the attribute, e.g. float, int or some sort of container.
 *	\param attr_range		the constraints used when editing the attribute, see VarRange for more details.
 *  \param attr_var_name	the name of the attribute.
 *	\param attr_name		a string that provides a more readable name used for GUI applications.
 */
#define CE_DECLARE_CONST_ATTRIBUTE(attr_type, attr_range, attr_var_name, attr_name) \
    public: \
        CE_DECLARE_ATTRIBUTE_GETTER(attr_type, attr_range, attr_var_name, attr_name) \
    protected: \
		attr_type __m_##attr_var_name; \
        CE_DECLARE_NAME_TYPE(attr_var_name##AttributeName, attr_name); \
        typedef Attribute<attr_type, attr_var_name##AttributeName, attr_range> attr_var_name##Attribute; \

/*! \brief Defines a getter function. It is mostly for convenience, so that you write this sort of code by yourself.
*	\param attr_type		the basic type of the attribute, e.g. float, int or some sort of container.
 *	\param attr_range		the constraints used when editing the attribute, see VarRange for more details.
 *  \param attr_var_name	the name of the attribute.
 *	\param attr_name		a string that provides a more readable name used for GUI applications.
 */
#define CE_DECLARE_ATTRIBUTE_GETTER(attr_type, attr_range, attr_var_name, attr_name) \
	attr_type get##attr_var_name() const { return CE_GET_ATTRIBUTE(attr_var_name); }

/*! \brief Defines a setter function. It is mostly for convenience, so that you write this sort of code by yourself.
*	\param attr_type		the basic type of the attribute, e.g. float, int or some sort of container.
 *	\param attr_range		the constraints used when editing the attribute, see VarRange for more details.
 *  \param attr_var_name	the name of the attribute.
 *	\param attr_name		a string that provides a more readable name used for GUI applications.
 */
#define CE_DECLARE_ATTRIBUTE_SETTER(attr_type, attr_range, attr_var_name, attr_name) \
	void set##attr_var_name(const attr_type& t) { CE_GET_ATTRIBUTE(attr_var_name) = t; }

/*! \brief Declares an attribute with its own getter and setter function.
 *	\param attr_type		the basic type of the attribute, e.g. float, int or some sort of container.
 *	\param attr_range		the constraints used when editing the attribute, see VarRange for more details.
 *  \param attr_var_name	the name of the attribute.
 *	\param attr_name		a string that provides a more readable name used for GUI applications.
 */
#define CE_DECLARE_ATTRIBUTE(attr_type, attr_range, attr_var_name, attr_name) \
    public: \
        CE_DECLARE_ATTRIBUTE_SETTER(attr_type, attr_range, attr_var_name, attr_name) \
    CE_DECLARE_CONST_ATTRIBUTE(attr_type, attr_range, attr_var_name, attr_name) \

// TODO in VS2013: initializer list
template<typename TConf, typename T, typename... TArgs>
inline void InitAttributeList(const TConf& attr_list, const T& var, const TArgs&... args)
{
    attr_list->addAttribute(var);
    InitAttributeList(attr_list, args...); 
}

template<typename TConf, typename T>
inline void InitAttributeList(const TConf& attr_list, const T& var)
{
    attr_list->addAttribute(var);
}

#define CE_CREATE_ATTRIBUTE(var) make_aligned_shared<var##Attribute>(CE_GET_ATTRIBUTE(var))
#define CE_CREATE_CUSTOM_ATTRIBUTE(attr, ...) make_aligned_shared<attr>(__VA_ARGS__)

#define CE_POLYMORPHIC virtual
#define CE_STATIC inline

#define CE_ATTRIBUTES(attr_class, base_class, ...) \
	attr_class void getAttributeList(const AttributeListPtr& attr_list) { \
		base_class::getAttributeList(attr_list); \
		InitAttributeList(attr_list, __VA_ARGS__); \
	} 

#define CE_NO_ATTRIBUTES(attr_class, base_class) \
	attr_class void getAttributeList(const AttributeListPtr& conf) { base_class::getAttributeList(conf); }

}

#endif /* _CARINA_ATTRIBUTE_HH_ */
